package com.cksys.achievement.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cksys.achievement.entity.Role;
import com.cksys.achievement.entity.User;
import com.cksys.achievement.entity.UserRole;
import com.cksys.achievement.enums.EmailStatusEnum;
import com.cksys.achievement.enums.RegisterEnum;
import com.cksys.achievement.enums.Roles;
import com.cksys.achievement.enums.SexEnum;
import com.cksys.achievement.service.*;
import com.cksys.achievement.service.impl.CustomUserDetailsServiceImpl;
import com.cksys.achievement.utils.CacheUtils;
import com.cksys.achievement.utils.EmailUtil;
import com.cksys.achievement.utils.FileUtils;
import com.cksys.achievement.utils.RestResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author tadechen
 * @since 2019-09-26
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    IUserService userService;

    @Autowired
    private CustomUserDetailsServiceImpl customUserDetailsServiceImpl;

    @Autowired
    BCryptPasswordEncoder encoder;

    @Autowired
    FileUtils fileUtils;

    @Autowired
    IUserRoleService userRoleService;

    @Autowired
    IRoleService roleService;

    @Autowired
    ISettingService settingService;

    @Autowired
    private EmailUtil emailUtil;

    @Autowired
    private CacheUtils cacheUtils;

    @GetMapping("/get/me")
    public RestResponse getMe(@AuthenticationPrincipal UserDetails userDetails) {
        String username = userDetails.getUsername();
        if (null != username) {
            User user = userService.getByUserName(username);
            return RestResponse.ok(user);
        }
        return RestResponse.fail();
    }

    @PostMapping("/register")
    public RestResponse register(@Valid User user,
                                 @RequestParam(value = "file", required = false) MultipartFile file) throws IOException {
        if (userService.count(new LambdaQueryWrapper<User>().eq(User::getUsername, user.getUsername())) > 0) {
            return RestResponse.fail("用户名被占用！");
        }

        if (null == file) {
            user.setAvatarPath("/images/logo.png");
        }

        if (null != user.getEmail() && !"".equals(user.getEmail())) {
            int count = userService.count(new LambdaQueryWrapper<User>().eq(User::getEmail, user.getEmail()));
            if (count > 0) {
                return RestResponse.fail("邮箱被占用！");
            }
            Integer flag = (int)(Math.random() * 8998) + 1000 + 1;
            cacheUtils.put("emailFlag", String.valueOf(flag), 5);
            cacheUtils.put("username", user.getUsername(), 5);
            String url = EmailController.EMAIL_ACTIVATION;
            url = String.format(url, user.getEmail(), flag);
            String content = emailUtil.regTemplate.replace("${name}", user.getUsername()).replace("${link}", url).replace("${link}", url);
            boolean b = emailUtil.sendHTMLEmail("邮箱绑定", content, user.getEmail());
            if (b) {
                user.setEmailFlag(EmailStatusEnum.NO_ACTIVATION);
            }
        }

        user.setQqId(null).setAccountId(null);
        Boolean autoRegister = settingService.getById(1).getIsRegister();
        if (autoRegister) {
            user.setRegister(RegisterEnum.REGISTER_YES);
        } else {
            user.setRegister(RegisterEnum.UN_REGISTER);
        }

        user.setPassword(encoder.encode(user.getPassword().trim()));
        boolean save = userService.save(user);

        if (null != file) {
            String path = fileUtils.saveImg(file, user);
            user.setAvatarPath(path);
            userService.updateById(user);
        }

        if (save) {
            userRoleService.saveOrUpdate(new UserRole().setUserId(user.getId()).setRoleId(1), new LambdaQueryWrapper<UserRole>().eq(UserRole::getUserId, user.getId()));
            user.setPassword(null);
            return RestResponse.ok(user);
        } else {
            return RestResponse.fail("注册失败！");
        }
    }

    @PostMapping("/logo")
    public RestResponse uploadLogo(@RequestParam("id") Integer id,
                                   @RequestParam("file") MultipartFile file) throws IOException {
        User user = new User().setId(id);
        String s = fileUtils.saveImg(file, user);
        user.setAvatarPath(s);
        boolean b = userService.updateById(user);
        if (b) {
            return RestResponse.ok();
        } else {
            return RestResponse.fail();
        }
    }

    @PostMapping("/update")
    public RestResponse<User> updateUser(User user,
                                         @RequestParam(value = "checkPassword", required = false) String checkPassword,
                                         @AuthenticationPrincipal UserDetails userDetails,
                                         HttpServletRequest request) {
        User nowUser = userService.getByUserName(userDetails.getUsername());
        if (nowUser == null) {
            throw new RuntimeException("用户错误!");
        }

        if (user.getUsername() != null) {
            User byUserName = userService.getByUserName(user.getUsername());
            if (byUserName != null) {
                if (!byUserName.getUsername().equals(user.getUsername())) {
                    return RestResponse.fail("用户名已被占用！");
                }
            }
        }

        if (!nowUser.getId().equals(user.getId())) {
            throw new RuntimeException("您不能修改他人！");
        }

        if (user.getPassword() != null && !"".equals(user.getPassword())) {
            if (!user.getPassword().equals(checkPassword)) {
                throw new RuntimeException("俩次密码不一致！");
            } else {
                user.setPassword(encoder.encode(user.getPassword()));
            }
        } else {
            user.setPassword(null);
        }

        boolean b = userService.updateById(user);
        if (b) {
            user = userService.getById(user.getId());
            String username = user.getUsername();
            UserDetails userDetails1 = customUserDetailsServiceImpl.loadUserByUsername(username);
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userDetails1, authentication.getCredentials(), userDetails.getAuthorities());
            auth.setDetails(authentication.getDetails());
            SecurityContextHolder.getContext().setAuthentication(auth);
            return RestResponse.ok(user);
        } else  {
            return RestResponse.fail("更新失败！");
        }

    }

    @GetMapping("/others")
    public RestResponse<List<User>> getOherUsers(@RequestParam(value = "userIds", required = false) List<Integer> userIds) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if (userIds != null && userIds.size() > 0) {
            queryWrapper.notIn("id", userIds);
        }
        queryWrapper.select("id", "username", "avatar_path", "real_name");
        return RestResponse.ok(userService.list(queryWrapper));
    }

    @GetMapping("/others/noMe")
    public RestResponse<List<User>> getOherUsersNoMe(@RequestParam(value = "userIds", required = false) List<Integer> userIds,
                                                     @AuthenticationPrincipal UserDetails userDetails) {
        User user = userService.getByUserName(userDetails.getUsername());
        if (user == null) {
            throw new RuntimeException("用户错误!");
        }

        userIds.add(user.getId());
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if (userIds != null && userIds.size() > 0) {
            queryWrapper.notIn("id", userIds);
        }
        queryWrapper.select("id", "username", "avatar_path", "real_name");
        return RestResponse.ok(userService.list(queryWrapper));
    }

    @GetMapping("/getId")
    public RestResponse<List<User>> getIdByRealName(@RequestParam("realName") String realName) {
        List<User> users = userService.list(new LambdaQueryWrapper<User>().eq(User::getRealName, realName).select(User::getId, User::getUsername, User::getRealName, User::getAvatarPath));
        if (users.size() > 0) {
            return RestResponse.ok(users);
        }
        return RestResponse.fail("没有此人！");
    }

    @PostMapping("/findPassword")
    public RestResponse findPassword(@Valid @NotEmpty @RequestParam("username") String username,
                                     @Valid @NotEmpty @RequestParam("password") String password,
                                     @Valid @NotEmpty @RequestParam("code") String code) {
        String findPasswordCode = (String) cacheUtils.get("findPasswordCode");
        if (null == findPasswordCode) {
            return RestResponse.fail("验证码失效！");
        }
        if (!findPasswordCode.equalsIgnoreCase(code)) {
            return RestResponse.fail("验证码错误！");
        }
        Integer id = userService.getByUserName(username).getId();
        if (null == id) {
            return RestResponse.fail("用户为空！");
        }
        password = encoder.encode(password.trim());
        boolean b = userService.updateById(new User().setId(id).setPassword(password));
        if (b) {
            cacheUtils.remove("findPasswordCode");
            UserDetails userDetails1 = customUserDetailsServiceImpl.loadUserByUsername(username);
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userDetails1, authentication.getCredentials(), authentication.getAuthorities());
            auth.setDetails(authentication.getDetails());
            SecurityContextHolder.getContext().setAuthentication(auth);
            return RestResponse.ok();
        } else {
            return RestResponse.fail("找回失败！");
        }
    }

    /*---------------管理员开始---------------*/

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @GetMapping("/count")
    public RestResponse countUser() {
        return RestResponse.ok(userService.count());
    }

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @PostMapping("/search")
    public RestResponse search(@RequestParam(value = "page", required = false, defaultValue = "1") Integer page,
                               @RequestParam(value = "size", required = false, defaultValue = "10") Integer size,
                               @RequestParam(value = "username", required = false) String username,
                               @RequestParam(value = "realName", required = false) String realName,
                               @RequestParam(value = "sex", required = false) SexEnum sexEnum,
                               @RequestParam(value = "roles", required = false) Roles roles,
                               @RequestParam(value = "register", required = false) RegisterEnum registerEnum,
                               @AuthenticationPrincipal UserDetails userDetails) {
        boolean isSuper = userDetails.getAuthorities().contains(new SimpleGrantedAuthority(Roles.ROLE_SUPER.getRoleName()));
        if (!isSuper) {
            roles = null;
        }
        IPage<User> userIPage = userService.searchUser(new Page<>(page, size), username, realName, sexEnum, roles, registerEnum);
        return RestResponse.ok(userIPage);
    }

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @PostMapping("/delete/{id}")
    public RestResponse delete(@PathVariable Integer id) {
        try {
            Boolean b = null;
            b = userService.removeUser(id);
            if (b) {
                return RestResponse.ok();
            } else {
                return RestResponse.fail("管理员不能删除!");
            }
        } catch (Exception e) {
            return RestResponse.fail(e.getMessage());
        }
    }

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @PostMapping("/delete/more")
    public RestResponse deleteMore(@RequestParam("ids[]") List<Integer> ids) {
        try {
            Boolean b = null;
            b = userService.removeUsers(ids);
            if (b) {
                return RestResponse.ok();
            } else {
                return RestResponse.fail();
            }
        } catch (Exception e) {
            return RestResponse.fail(e.getMessage());
        }
    }

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @PostMapping("/audited/{id}")
    public RestResponse audited(@PathVariable Integer id,
                                    @RequestParam("register") RegisterEnum registerEnum,
                                @AuthenticationPrincipal UserDetails userDetails) {
        Integer roleId = userRoleService.getOne(new LambdaQueryWrapper<UserRole>().eq(UserRole::getUserId, id)).getRoleId();
        if (roleId == 3) {
            return RestResponse.fail("超级管理员不能修改！");
        }
        if (roleId == 2) {
            if (!userDetails.getAuthorities().contains(new SimpleGrantedAuthority(Roles.ROLE_SUPER.getRoleName()))) {
                return RestResponse.fail("无权修改");
            }
        }
        User user = new User().setId(id).setRegister(registerEnum);
        boolean b = userService.updateById(user);
        if (b) {
            return RestResponse.ok();
        } else {
            return RestResponse.fail("修改失败！");
        }
    }

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @PostMapping("/audited/more")
    public RestResponse audited(@RequestParam("ids[]") List<Integer> ids,
                                @RequestParam("register") RegisterEnum registerEnum,
                                @AuthenticationPrincipal UserDetails userDetails) {
        List<UserRole> list = userRoleService.list(new LambdaQueryWrapper<UserRole>().in(UserRole::getUserId, ids));
        long count = list.stream().filter(t -> t.getRoleId() == 3).count();
        if (count > 0) {
            return RestResponse.fail("超级管理员不能修改！");
        }
        count = list.stream().filter(t -> t.getRoleId() == 2).count();
        if (count > 0) {
            if (!userDetails.getAuthorities().contains(new SimpleGrantedAuthority(Roles.ROLE_SUPER.getRoleName()))) {
                return RestResponse.fail("无权修改");
            }
        }
        List<User> users = ids.stream().map(t -> new User().setId(t).setRegister(registerEnum)).collect(Collectors.toList());
        boolean b = userService.updateBatchById(users);
        if (b) {
            return RestResponse.ok();
        } else {
            return RestResponse.fail("修改失败！");
        }
    }


    /*---------------超级管理员开始---------------*/

    @PreAuthorize("hasRole('ROLE_SUPER')")
    @PostMapping("/root/{id}")
    public RestResponse root(@PathVariable Integer id,
                             @RequestParam("role") Roles roles) {
        if (roles.equals(Roles.ROLE_SUPER)) {
            return RestResponse.fail("不能赋予超级管理员权限！");
        }
        Integer rId = roleService.getOne(new LambdaQueryWrapper<Role>().eq(Role::getName, roles.getRoleName()).select(Role::getId)).getId();
        boolean b = userRoleService.saveOrUpdate(new UserRole().setUserId(id).setRoleId(rId),
                new LambdaQueryWrapper<UserRole>().eq(UserRole::getUserId, id));
        if (b) {
            return RestResponse.ok();
        } else {
            return RestResponse.fail();
        }
    }
}
